﻿using System;
using System.Xml;
namespace Moonlite.Services.Web.Rss
{
    public class RssReader
    {
        public event EventHandler FeedLoaded;
        public event EventHandler RssNodeFound;
        public event EventHandler ChannelNodeFound;
        public event EventHandler RssItemAdded;
        public event RssReaderErrorEventHandler Error;
        private string rootNodeName = "rss";
        public string RootNodeName
        {
            get { return rootNodeName; }
            set { rootNodeName = value; }
        }

        private string channelNodeName = "channel";
        public string ChannelNodeName
        {
            get { return channelNodeName; }
            set { channelNodeName = value; }
        }

        private bool rdfMode;
        public bool RdfMode
        {
            get { return rdfMode; }
            set
            {
                rootNodeName = value ? "rdf:RDF" : "rss";
                rdfMode = value;
            }
        }

        public static RssFeed GetFeed(string url)
        {
            RssReader reader = new RssReader();
            return reader.Retrieve(url);
        }

        public RssFeed Retrieve(string url)
        {
            RssFeed feed = new RssFeed { Items = new RssItemCollection() };
            try
            {
                using (XmlTextReader urlReader = new XmlTextReader(url))
                {
                    XmlDocument document = new XmlDocument();
                    document.Load(urlReader);
                    OnFeedLoaded(EventArgs.Empty);
                    XmlNode rssFeedNode = null;
                    foreach (XmlNode childNode in document.ChildNodes)
                    {
                        if (childNode.Name.ToLower() != RootNodeName.ToLower() || childNode.ChildNodes.Count <= 0)
                            continue;

                        rssFeedNode = childNode;
                        OnRssNodeFound(EventArgs.Empty);
                        break;
                    }

                    if (rssFeedNode != null)
                    {
                        XmlNode channelNode = null;
                        foreach (XmlNode childNode in rssFeedNode.ChildNodes)
                        {
                            if (childNode.Name.ToLower() != ChannelNodeName.ToLower() || childNode.ChildNodes.Count <= 0)
                                continue;

                            channelNode = childNode;
                            OnChannelNodeFound(EventArgs.Empty);
                            break;
                        }

                        if (channelNode != null)
                        {
                            foreach (XmlNode childNode in channelNode.ChildNodes)
                            {
                                switch (childNode.Name.ToLower())
                                {
                                    case "title":
                                        feed.Title = childNode.InnerText;
                                        break;
                                    case "description":
                                        feed.Description = childNode.InnerText;
                                        break;
                                    case "language":
                                        feed.Language = childNode.InnerText;
                                        break;
                                    case "copyright":
                                        feed.Copyright = childNode.InnerText;
                                        break;
                                    case "webmaster":
                                        feed.Webmaster = childNode.InnerText;
                                        break;
                                    case "pubdate":
                                        feed.PublishDate = childNode.InnerText;
                                        break;
                                    case "lastbuilddate":
                                        feed.LastBuildDate = childNode.InnerText;
                                        break;
                                    case "category":
                                        feed.Category = childNode.InnerText;
                                        break;
                                    case "generator":
                                        feed.Generator = childNode.InnerText;
                                        break;
                                    case "ttl":
                                        feed.TimeToLive = childNode.InnerText;
                                        break;
                                    case "rating":
                                        feed.Rating = childNode.InnerText;
                                        break;
                                    case "skiphours":
                                        feed.SkipHours = childNode.InnerText;
                                        break;
                                    case "skipdays":
                                        feed.SkipDays = childNode.InnerText;
                                        break;
                                    case "managingeditor":
                                        feed.ManagingEditor = childNode.InnerText;
                                        break;
                                    case "item":
                                        feed.Items.Add(GetRssItem(childNode));
                                        OnRssItemAdded(EventArgs.Empty);
                                        break;
                                    default:
                                        break;
                                }
                            }

                            if (RdfMode == true)
                            {
                                foreach (XmlNode childNode in rssFeedNode.ChildNodes)
                                {
                                    switch (childNode.Name.ToLower())
                                    {
                                        case "item":
                                            feed.Items.Add(GetRssItem(childNode));
                                            OnRssItemAdded(EventArgs.Empty);
                                            break;
                                        default:
                                            break;
                                    }
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception exception)
            {
                feed.ErrorMessage = "Error parsing Rss feed: " + exception.Message;
                OnError(new RssReaderErrorEventArgs(exception.Message));
            }

            return feed;
        }

        private static RssItem GetRssItem(XmlNode node)
        {
            RssItem item = new RssItem();
            foreach (XmlNode childNode in node.ChildNodes)
            {
                switch (childNode.Name.ToLower())
                {
                    case "title":
                        item.Title = childNode.InnerText;
                        break;
                    case "description":
                        item.Description = childNode.InnerText;
                        break;
                    case "link":
                        item.Link = childNode.InnerText;
                        break;
                    case "author":
                        item.Author = childNode.InnerText;
                        break;
                    case "comments":
                        item.Comments = childNode.InnerText;
                        break;
                    case "pubdate":
                        item.PublishDate = childNode.InnerText;
                        break;
                    case "guid":
                        item.Guid = childNode.InnerText;
                        break;
                    default:
                        break;
                }
            }

            return item;
        }

        public virtual void OnFeedLoaded(EventArgs e)
        {
            EventHandler handler = FeedLoaded;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        public virtual void OnRssNodeFound(EventArgs e)
        {
            EventHandler handler = RssNodeFound;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        public virtual void OnChannelNodeFound(EventArgs e)
        {
            EventHandler handler = ChannelNodeFound;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        public virtual void OnRssItemAdded(EventArgs e)
        {
            EventHandler handler = RssItemAdded;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        public virtual void OnError(RssReaderErrorEventArgs e)
        {
            RssReaderErrorEventHandler handler = Error;
            if (handler != null)
            {
                handler(this, e);
            }
        }
    }
}
